package cn.xinfei.xdecision.common.service.enginex.impl;

import cn.xinfei.xdecision.common.model.enginex.po.EngineNode;
import cn.xinfei.xdecision.common.service.enginex.util.NodeUtil;
import cn.xinfei.xdecision.common.utils.enums.enginex.NodeTypeEnum;
import cn.xinfei.xdecision.common.utils.util.StringUtil;
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import org.springframework.stereotype.Service;

import java.util.Objects;

import static cn.xinfei.xdecision.common.utils.constant.enginex.DecisionFlowCheckMessageConst.*;
import static cn.xinfei.xdecision.common.utils.exception.enums.ErrorCodeEnum.DECISION_FLOW_NODE_CHECK_ERROR;
import static cn.xinfei.xdecision.common.utils.exception.util.ApiExceptionUtil.exception;

/**
 * 节点配置属性完整性校验
 * <p>
 * Created by maying on 2023/04/25.
 **/
@Service
public class NodeJsonCheckService {

    public void nodeJsonCheck(EngineNode node) {
        if (!NodeUtil.IGNORE_NODE_JSON_CHECK_NODE_TYPES.contains(node.getNodeType()) && StringUtil.isBlank(node.getNodeJson())) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), node.getNodeCode(), BASE_RULE_NODE_JSON_IS_NULL);
        }
        NodeTypeEnum nodeTypeEnum = NodeTypeEnum.adapad(node.getNodeType());
        switch (nodeTypeEnum) {
            case RULE_BASE:
                ruleBaseCheck(node);
                return;
            case SCORECARD:
            case DECISION_TABLES:
            case DECISION_TREE:
                componentListCheck(node);
                return;
            case CLASSIFY:
            case SANDBOX:
            case CHAMPION_CHALLENGE:
            case PARALLEL:
                branchSizeCheck(node);
                return;
            case DECISION:
                //决策条件、决策结果、默认结果、输出变量不能为空
                decisionCheck(node);
                return;
            case ACTION:
                //动作校验
                actionCheck(node);
                return;
            default:
        }
    }

    private void actionCheck(EngineNode node) {
        JSONObject jo = JSON.parseObject(node.getNodeJson());
        Long postType = jo.getLong("postType");
        Long actionServer = jo.getLong("actionServer");
        String actionInterface = jo.getString("actionInterface");
        if (Objects.isNull(postType) || Objects.isNull(actionServer) || Objects.isNull(actionInterface)) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), node.getNodeCode(), ACTION_INCOMPLETE);
        }
    }

    private void branchSizeCheck(EngineNode node) {
        String nodeCode = node.getNodeCode();
        NodeTypeEnum nodeTypeEnum = NodeTypeEnum.adapad(node.getNodeType());
        switch (nodeTypeEnum) {
            case CLASSIFY:
                JSONObject nj1 = JSONObject.parseObject(node.getNodeJson());
                JSONArray conditions = nj1.getJSONArray("conditions");
                if (conditions == null || conditions.size() < 2) {
                    throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), nodeCode, INTEGRITY_CHECK_RULE_BRANCH_NODE_SIZE_ERROR);
                }
                break;
            case SANDBOX://比例相加=100
                JSONArray nj2 = JSONArray.parseArray(node.getNodeJson());
                int size = nj2.size();
                int sum = 0;

                for (int i = 0; i < size; i++) {
                    JSONObject sandBox = nj2.getJSONObject(i);
                    sum += sandBox.getIntValue("proportion");
                }
                if (nj2.size() < 2 || sum != 100) {
                    throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), nodeCode, INTEGRITY_CHECK_RULE_BRANCH_NODE_SIZE_ERROR);
                }
                break;
            case CHAMPION_CHALLENGE:
            case PARALLEL:
                JSONArray nj3 = JSONArray.parseArray(node.getNodeJson());
                if (nj3 == null || nj3.size() < 2) {
                    throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), nodeCode, INTEGRITY_CHECK_RULE_BRANCH_NODE_SIZE_ERROR);
                }
                break;
            default:
                break;
        }
    }

    /**
     * 规则库节点校验
     */
    private void ruleBaseCheck(EngineNode node) {
        JSONObject nodeJson = JSONObject.parseObject(node.getNodeJson());
        Integer groupType = nodeJson.getInteger("groupType");
        Integer isPartnerTraining = nodeJson.getInteger("isPartnerTraining");
        if (1 == groupType && isPartnerTraining == null) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), node.getNodeCode(), INTEGRITY_CHECK_RULE_ISPARTNERTRAINING_NULL);
        }
        componentListCheck(node);
        terminationInfoCheck(node);
    }

    /**
     * 决策选项节点校验
     */
    private void decisionCheck(EngineNode node) {
        String nodeCode = node.getNodeCode();
        JSONObject nodeJson = JSONObject.parseObject(node.getNodeJson());
        JSONArray conditions = nodeJson.getJSONArray("conditions");
        if (conditions == null || conditions.isEmpty()) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), nodeCode, INTEGRITY_CHECK_RULE_CONDITIONS_MISSING);
        }

        for (int i = 0; i < conditions.size(); i++) {
            JSONObject condition = conditions.getJSONObject(i);
            String result = condition.getString("result");
            JSONArray formulas = condition.getJSONArray("formula");
            if (StringUtil.isBlank(result)) {
                throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), nodeCode, INTEGRITY_CHECK_RULE_DECISION_NODE_RESULT_MISSING);
            }
            if (formulas == null || formulas.isEmpty()) {
                throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), nodeCode, INTEGRITY_CHECK_RULE_CONDITIONS_MISSING);
            }

            for (int j = 0; j < formulas.size(); j++) {
                JSONObject formula = formulas.getJSONObject(j);
                conditionCheck(node.getNodeName(), nodeCode, formula);
            }
        }

        JSONObject output = nodeJson.getJSONObject("output");
        decisionNodeOutputCheck(node.getNodeName(), nodeCode, output);
    }

    /**
     * 决策选项节点输出结果校验
     */
    private void decisionNodeOutputCheck(String nodeName, String nodeCode, JSONObject output) {
        if (output == null || output.isEmpty()
                || StringUtil.isBlank(output.getString("fieldCode"))
                || StringUtil.isBlank(output.getString("fieldName"))
                || StringUtil.isBlank(output.getString("defaultValue"))) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, nodeName, nodeCode, INTEGRITY_CHECK_RULE_CONDITIONS_MISSING);
        }
    }

    /**
     * 表达式校验
     */
    private void conditionCheck(String nodeName, String nodeCode, JSONObject condition) {
        if (StringUtil.isBlank(condition.getString("fieldCode"))
                || StringUtil.isBlank(condition.getString("fieldName"))
                || StringUtil.isBlank(condition.getString("operator"))
                || StringUtil.isBlank(condition.getString("value"))
                || Objects.isNull(condition.getInteger("valueType"))) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, nodeName, nodeCode, INTEGRITY_CHECK_RULE_CONDITIONS_MISSING);
        }
    }

    /**
     * 策略节点的已选组件列表校验
     */
    private void componentListCheck(EngineNode node) {
        JSONObject nodeJson = JSONObject.parseObject(node.getNodeJson());
        JSONArray componentList = nodeJson.getJSONArray("componentList");
        if (componentList == null || componentList.isEmpty()) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), node.getNodeCode(), INTEGRITY_CHECK_RULE_COMPONENT_LIST_NULL);
        }
    }

    /**
     * 终止条件配置校验
     */
    private void terminationInfoCheck(EngineNode node) {
        JSONObject nodeJson = JSONObject.parseObject(node.getNodeJson());
        JSONObject terminationInfo = nodeJson.getJSONObject("terminationInfo");
        if (terminationInfo == null) {
            throw exception(DECISION_FLOW_NODE_CHECK_ERROR, node.getNodeName(), node.getNodeCode(), INTEGRITY_CHECK_RULE_TERMINATION_INFO_NULL);
        }
    }
}
